﻿#pragma once
#include "ReMeshApp.h"

#include "AddOutput.h"
#include "CommandLine.h"
#include "CreateExecutioner.h"
#include "CreateMeshAction.h"
#include "CreateSystemAction.h"
#include "AddUserObject.h"

#include "Parser.h"

// TODO
#include "Executioner.h"
#include "Mesh.h"
//

#include <vtkm/cont/Invoker.h>
#include <vtkm/cont/RuntimeDeviceInformation.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>

#ifdef VTKM_ENABLE_TBB
#include "tbb/task_scheduler_init.h"
#endif //  VTKM_ENABLE_TBB

void ReMeshApp::PrintLogo()
{
  std::string logo = R"(
╔════════════════════════════╗
║●程序简介：                                             
║  可压缩多介质大变形流体二维模拟程序——TriAngels。         
║  基于三角形网格拉格朗日有限体积方法，采用网格动态局域重    
║  分处理网格大变形，采用物质补偿流动方法缓解物理量振荡。    
║  更详细介绍，请参见文献[1,2]。                          
║●程序贡献者：                                           
║  主编  肖  波                                
║  方法和物理讨论  赵海波，赵  梨，柏劲松，王刚华，谢  龙  
║                  舒  适，冯春生，段书超，阚明先，等     
║                                                      
║●代码权限：                                            
║  TriAngels 代码为开源代码，欢迎获取和改进代码。         
║  如果在您的科研工作中用到了本程序或者其中的算法，请引用   
║  文献[1] ，谢谢。                                     
║                                                      
║参考文献                                               
║[1] 赵海波，多介质可压缩流体大变形运动的二维拉格朗日模拟   
║研究，硕士论文，2018，中国工程物理研究院.                
║[2] 赵海波, 肖波, 柏劲松, 等，拉氏方法模拟二维多介质可压   
║缩流体的运动，高压物理学报，2018，Vol. 32(4):042303.     
║                                                      
║                            肖波(homenature@139.com)  
║                               流体物理研究所 绵阳 四川 
║                                            20210711
╚════════════════════════════╝
)";
  std::cout << logo << std::endl;
}
void ReMeshApp::Run()
{

  //vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(vtkm::cont::DeviceAdapterTagSerial{});
  PrintLogo();
  ParseCLI();
  ParseInputFile();
  //SetupDevice();

  RunExecutioner();
}

void ReMeshApp::RunExecutioner()
{
  if (!_parser)
  {
    Error("配置文件未建立，不能设置设备信息");
  }

  vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny{};

  auto& tracker = vtkm::cont::GetRuntimeDeviceTracker();
  _parser->SetPrefix("Parallel/");
  auto type = _parser->Get<std::string>("type");
  if (type == "serial")
  {
    device = vtkm::cont::DeviceAdapterTagSerial{};
    Info("串行模式运行........");
  }
  else if (type == "tbb")
  {
    device = vtkm::cont::DeviceAdapterTagTBB{};
    if (tracker.CanRunOn(device))
    {
      Info("TBB并行模式运行........");
    }
    else
    {
      Warning("不支持TBB模式并行，切换为串行模式");
      device = vtkm::cont::DeviceAdapterTagSerial{};
    }
  }
  else if (type == "cuda")
  {
    device = vtkm::cont::DeviceAdapterTagCuda{};
    if (tracker.CanRunOn(device))
    {
      Info("cuda/gpu并行模式运行........");
    }
    else
    {
      Warning("不支持Cuda模式并行，切换为串行模式");
      device = vtkm::cont::DeviceAdapterTagSerial{};
    }
  }
  else
  {
    Warning("未知并行模式:", type, ", 切换为串行模式....");
    device = vtkm::cont::DeviceAdapterTagSerial{};
  }

#ifdef VTKM_ENABLE_TBB
  int num_threads = _parser->Get<int>("num_threads");
  int max_threads = tbb::task_scheduler_init::default_num_threads();
  if (num_threads > max_threads)
  {
    Warning("系统核数 < 设置核数");
    num_threads = max_threads;
  }
  std::cout << " 系统最大核数：" << max_threads << " 当前运行核数：" << num_threads << std::endl;

  tbb::task_scheduler_init init(num_threads);
#endif

  tracker.ForceDevice(device);

  // TODO: 外部调用
  _mesh->Build();
  _mesh->BuildFaceTopo();

  _executioner->Init();
  _executioner->Execute();
  //
}

void ReMeshApp::ParseInputFile()
{
  _ifile = _command_line->GetValue<std::string>("-i");
  _parser = std::make_shared<Parser>(_ifile);

  _awh.push_back(std::make_shared<CreateMeshAction>(*this));
  _awh.push_back(std::make_shared<CreateSystemAction>(*this));
  _awh.push_back(std::make_shared<CreateExecutioner>(*this));
  _awh.push_back(std::make_shared<AddOutputAction>(*this));
  _awh.push_back(std::make_shared<AddUserObjectAction>(*this));

  for (auto& action : _awh)
  {
    action->Execute();
  }
}

void ReMeshApp::SetupDevice()
{
  if (!_parser)
  {
    Error("配置文件未建立，不能设置设备信息");
  }

  vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny{};

  auto& tracker = vtkm::cont::GetRuntimeDeviceTracker();
  _parser->SetPrefix("Parallel/");
  auto type = _parser->Get<std::string>("type");
  if (type == "serial")
  {
    device = vtkm::cont::DeviceAdapterTagSerial{};
    Info("串行模式运行........");
  }
  else if (type == "tbb")
  {
    device = vtkm::cont::DeviceAdapterTagTBB{};
    if (tracker.CanRunOn(device))
    {
      Info("TBB并行模式运行........");
    }
    else
    {
      Warning("不支持TBB模式并行，切换为串行模式");
      device = vtkm::cont::DeviceAdapterTagSerial{};
    }
  }
  else if (type == "cuda")
  {
    device = vtkm::cont::DeviceAdapterTagCuda{};
    if (tracker.CanRunOn(device))
    {
      Info("cuda/gpu并行模式运行........");
    }
    else
    {
      Warning("不支持Cuda模式并行，切换为串行模式");
      device = vtkm::cont::DeviceAdapterTagSerial{};
    }
  }
  else
  {
    Warning("未知并行模式:", type, ", 切换为串行模式....");
    device = vtkm::cont::DeviceAdapterTagSerial{};
  }

#ifdef VTKM_ENABLE_TBB
  int num_threads = _parser->Get<int>("num_threads");
  int max_threads = tbb::task_scheduler_init::default_num_threads();
  if (num_threads > max_threads)
  {
    Warning("系统核数 < 设置核数");
    num_threads = max_threads;
  }
  std::cout << " 系统最大核数：" << max_threads << " 当前运行核数：" << num_threads << std::endl;

  tbb::task_scheduler_init init(num_threads);
#endif

  tracker.ForceDevice(device);
}
